#include "StdAfx.h"
#include "planet.h"
#include "game.h"
#include "player.h"
#include "PlanetInternalImpl.h"
#include <boost/foreach.hpp>
#include "GameState.h"
#include "MessageBuilder.h"
#include "FlightInfo.h"

const int timerInterval = 1000;

Game::Game(boost::shared_ptr<QGraphicsView> gameView, GameInitInfoPtr gameInitInfo, QObject* parent)
   : QObject(parent)
   , m_gameView(gameView)
   , m_planets(new PlanetsMap())
   , m_gameInitInfo(gameInitInfo)
   , m_playerID(gameInitInfo->GetPlayerID())
   , m_players(new PlayersMap())
{
   qDebug() << "Game::startGame" << GetCurrentThreadId();
}

Game::Game( boost::shared_ptr<QGraphicsView> gameView, QObject *parent /*= NULL*/ )
   : QObject(parent)
   , m_gameView(gameView)
   , m_planets(new PlanetsMap())
   , m_players(new PlayersMap())
{

}
Game::~Game()
{
   disconnectSignals();
}

void Game::initGame()
{
   initPlayers();
   initPlanets();
   initScene();
}

void Game::timerHandler()
{
   qDebug() << "Game::timerHandler" << GetCurrentThreadId();
   Update();
}

void Game::Update()
{
   PlanetPtr currentPlanet;

   foreach(const PlanetsPair& item, *m_planets)
   {
      currentPlanet = item.second;
      currentPlanet->increaseCountOfShips();
      currentPlanet->update();
   }
}

void Game::initPlanets()
{
   const PlanetsInitInfoVector& planetsInitInfo = m_gameInitInfo->PlanetsInitInfo();
   PlayerPtr playerPtr;
   BOOST_FOREACH(const PlanetInternalImplPtr& currentPlanet, planetsInitInfo)
   {
      if (currentPlanet->GetPlayerID() != undefinedPlayerID)
      {
         playerPtr = m_players->at(currentPlanet->GetPlayerID());
      }
      else
      {
         playerPtr = PlayerPtr();
      }
      m_planets->insert(PlanetsPair(currentPlanet->GetPlanetID(), 
                        boost::make_shared<Planet>(currentPlanet->GetPlanetID(), currentPlanet->GetX(), currentPlanet->GetY(),
                                                   currentPlanet->GetPlanetColor(), currentPlanet->GetPlanetTextureID(), 
                                                   currentPlanet->GetCountOfShips(), currentPlanet->GetPlanetSize(), playerPtr)));
   }
}

void Game::initPlayers()
{
   const PlayersInitInfoVector& playersInitInfo = m_gameInitInfo->PlayersInitInfo();
   BOOST_FOREACH(const PlayerInternalImplPtr& currentPlayer, playersInitInfo)
   {
      m_players->insert(PlayerPair(currentPlayer->GetPlayerID(), boost::make_shared<Player>(currentPlayer->GetPlayerID(),
                        currentPlayer->PlayerColor())));
   }
}

void Game::initScene()
{
   boost::shared_ptr<Planet> currentPlanet;

   foreach(const PlanetsPair& item, *m_planets)
   {
      currentPlanet = item.second;
      currentPlanet->setPos(currentPlanet->GetXCoord() * SceneRectWidth, currentPlanet->GetYCoord() * SceneRectHeight);
   }

   calculateDistancesAndAnglesBtwPlanets();

   m_gameScene = boost::make_shared<GameScene>(m_planets, m_players, m_playerID, m_distances, m_angles);
   m_gameScene->setItemIndexMethod(QGraphicsScene::NoIndex);
   m_gameView->setScene(m_gameScene.get());
   m_gameView->setDragMode(QGraphicsView::RubberBandDrag);
   m_gameScene->setSceneRect(QRectF(0,0,SceneRectWidth,SceneRectHeight));

   foreach(const PlanetsPair& item, *m_planets)
   {
      currentPlanet = item.second;
      m_gameScene->addItem(currentPlanet.get());
      QObject::connect(currentPlanet.get(), SIGNAL(hover(Planet*)), m_gameScene.get(), SLOT(updateLinksBtwPlanets(Planet*)));
      QObject::connect(currentPlanet.get(), SIGNAL(itemMouseRelease(PlanetPtr)), m_gameScene.get(), SLOT(planetClicked(PlanetPtr)));
   }

}

void Game::disconnectSignals()
{
   qDebug() << "Game destructor";
   disconnect();
   dumpObjectInfo();
}

void Game::StartLocalGame()
{
   initGame();
   startGame();
}

void Game::calculateDistancesAndAnglesBtwPlanets()
{
   const unsigned int countOfPlanets = m_planets->size();
   PlanetID p1ID;
   PlanetID p2ID;
   m_distances.resize(countOfPlanets);
   m_angles.resize(countOfPlanets);

   for ( GameScene::Distances::iterator iter = m_distances.begin(); iter != m_distances.end(); ++iter)
   {
      iter->resize(countOfPlanets);
   }
   for (GameScene::Angles::iterator iter = m_angles.begin(); iter != m_angles.end(); ++iter)
   {
      iter->resize(countOfPlanets);
   }

   foreach (const PlanetsPair& p1, *m_planets)
   {

      foreach (const PlanetsPair& p2, *m_planets)
      {
         p1ID = p1.first;
         p2ID = p2.first;
         if (p1ID == p2ID)
         {
            m_distances[p1ID][p2ID] = 0.0f;
            m_angles[p1ID][p2ID] = 0.0f;
            continue;
         }
         QPointF p1Center = p1.second->pos();
         QPointF p2Center = p2.second->pos();
         QLineF line(p1Center,p2Center);
         m_angles[p1ID][p2ID] = line.angle();

         if (p1ID < p2ID)
         {

            m_distances[p2ID][p1ID] = m_distances[p1ID][p2ID] = line.length();
         }
      }
   }
}


void Game::startGame()
{
   qDebug() << "Game::startGame" << GetCurrentThreadId();
   QObject::connect(&m_gameTimer, SIGNAL(timeout()), this, SLOT(timerHandler()));
   m_gameTimer.start(timerInterval);
}

void Game::shipsLaunched(const FlightInfoPtr flightInfoPtr)
{
   MessagePtr message = MessageBuilder::BuildShipsLaunchedMessage(flightInfoPtr);
   emit SendMessage(*message);
}

void Game::initLANGame()
{
   QObject::connect(m_gameScene.get(), SIGNAL(ShipsLaunched(const FlightInfoPtr)), 
      this, SLOT(shipsLaunched(const FlightInfoPtr)));
   QObject::connect(m_gameScene.get(), SIGNAL(ShipsArrivedToPlanet(const ShipsArrivedInfoPtr)), 
      this, SLOT(shipsArrived(const ShipsArrivedInfoPtr)));
}

void Game::StartLANGame(GameInitInfoPtr gameInitInfo)
{
   m_playerID = gameInitInfo->GetPlayerID();
   m_gameInitInfo = gameInitInfo;
   initGame();
   initLANGame();
   m_gameScene->invalidate();
}

void Game::SetGameState( GameStatePtr gameState )
{
   const GameState::PlanetsMap& planetsMap = gameState->GetPlanets();
   PlanetPtr currentPlanet;
   PlayerPtr player;
   PlanetInternalImplPtr currentPlanetInternal;

   BOOST_FOREACH(const GameState::PlanetsMapPair& planetPair, planetsMap)
   {
      currentPlanetInternal = planetPair.second;
      currentPlanet = m_planets->at(currentPlanetInternal->GetPlanetID());
      currentPlanet->CountOfShips(currentPlanetInternal->GetCountOfShips());
      if (currentPlanetInternal->GetPlayerID() != undefinedPlayerID)
      {
         player = m_players->at(currentPlanetInternal->GetPlayerID());
         currentPlanet->PlanetOwner(player);
      }
   }
   m_gameScene->update();
}

void Game::LaunchShips(const FlightInfoPtr flightInfoPtr)
{
   m_gameScene->LaunchShips(flightInfoPtr);
}

void Game::shipsArrived( const ShipsArrivedInfoPtr shipsArrivedInfoPtr)
{
   MessagePtr message = MessageBuilder::BuildShipsArrivedMessage(shipsArrivedInfoPtr);
   emit SendMessage(*message);
}